package mware_lib;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;

import mware_lib.NameService;
import mware_lib.StubSkeletonCom;

public class NameService_Stub extends NameService implements Runnable {
	/**
	 * hostname of name service
	 */
	private String hostName;
	/**
	 * Port of name service
	 */
	private int port;
	/**
	 * local map of all bound objects
	 */
	HashMap<String, Object> obj_map;
	ArrayList<Object> resolvList;
	/**
	 * thread of skeleton server
	 */
	Thread myself;
	boolean isRuning;
	ServerSocket serv_sock;
	Socket mySock;
	InputStream input_strm;
	BufferedReader in;
	OutputStream out;
	InputStreamReader in_low;
	/**
	 * port for the skeleton server
	 */
	int skel_local_port;

	public NameService_Stub(String serviceHost, int listenPort) {
		this.hostName = serviceHost;
		this.port = listenPort;
		this.isRuning = true;
	}

	@Override
	public void run() {
		//only accepts tcp connection, further operations in thread
		try {
			serv_sock = new ServerSocket(skel_local_port);

			while (isRuning) {
				System.out.println("Skeleton: warte auf daten..");
				mySock = serv_sock.accept();

				Thread t = new Thread(new SkeletonRequestExecuter(mySock, obj_map, hostName, port));
				t.start();
			}

			serv_sock.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	@Override
	/**
	 * local and remote rebind of an object
	 */
	public void rebind(Object servant, String name) {
//		if (true) throw new NullPointerException();
		String resu = null;
		if (obj_map == null) {
			skel_local_port = port+1;//skeleton_port
			obj_map = new HashMap<String, Object>();
			myself = new Thread(this);
			myself.start();
			
		}
		obj_map.put(name, servant);
		
		//bind name on remote name service
		InetAddress addr;
		try {
			addr = InetAddress.getLocalHost();
			byte[] ipAddr = addr.getAddress();
			String ownHostname = addr.getHostName();
			resu = StubSkeletonCom.sendIt("rebind:"+servant.getClass().getSuperclass().getName()+":"+name+":"+ownHostname+":"+skel_local_port, true, hostName, port);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		StubSkeletonCom.handleIfException(resu);
	}

	@Override
	public Object resolve(String name) {
		Object newobj = null;
		String incoming = null;
		try {
			//contact name service to get information about skeleton
			incoming = StubSkeletonCom.sendIt("resolve:"+name, true, hostName, port);
			if (incoming == null) {
				return null;
			}
			String[] splitted = incoming.split(":");
			if (splitted[0].equals("Class")) {
				
				//split on dot
				String[] className = splitted[1].split("\\u002E");
				String stubPosition = ("Manager".equals(className[className.length-1])?"branch_access.":"cash_access.");
				if (className.length <= 0)
					return null;
				String stubName = (stubPosition
						+ className[className.length - 1] + "_Stub");
				Class<?> newClass = Class.forName(stubName);
				Constructor<?>[] cs = newClass.getConstructors();
				
				//get hostname and port of message
				String skel_hostname = splitted[2];
				int skel_port = 0;
				try{
					skel_port = Integer.parseInt(splitted[3]);
				}catch(NumberFormatException e){
					System.out.println("get invalid port: "+splitted[2] + " !");
				}
				
				for (Constructor<?> cons : cs) {
					if (cons.getParameterTypes().length == 3) {
						newobj = cons.newInstance(name, skel_port, skel_hostname);
					}
				}
			} else if (splitted[0].equals("null")) {
				System.err
						.println(name
								+ " not found in name service!");
				throw new NullPointerException();
			}

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		StubSkeletonCom.handleIfException(incoming);
		return newobj;
	}
}
